package com.portal.settings;

import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.util.ReflectionUtils;

import javax.sql.DataSource;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 每分钟拉取配置数据
 *
 * @author Amadeus
 * @date 2025/02/26
 */
@Slf4j
public class SettingsAutoPullTask {

    /**
     * 插入默认值到数据库线程池，不阻塞启动
     */
    private static final ThreadPoolExecutor INIT_DEFAULT_VALUE_THREAD_POOL = new ThreadPoolExecutor(1, 1, 1, TimeUnit.MINUTES, new ArrayBlockingQueue<>(10000));

    @Setter
    private DataSource dataSource;


    @Setter
    private SettingsManager settingsManager;

    private Date lastDate;
    private long preTimeMillis;

    void doListenerAndAutoPull(List<Object> beanList, Collection<SettingsPullCallback> settingsPullCallbacks) {
        if (Boolean.FALSE.equals(Settings.getEnable())) {
            log.warn("settings is disabled, consider config property 'vgk.settings.enable=true'.");
            return;
        }
        // 检查注解，自动注册（代码来源参考，@xueqi @com.alibaba.cz.settings.settingHandler）
        log.warn("SettingsAutoPullTask 初始化开始");

        Set<Class<?>> clazzSet = new HashSet<>();
        // 初始化监听回调
        for (Object bean : beanList) {
            Class<?> realClazz = AopUtils.getTargetClass(bean);
            if (clazzSet.contains(realClazz)) {
                continue;
            }
            clazzSet.add(realClazz);
            initAddListener(realClazz, bean);
        }

        Settings.registerCallback(settingsPullCallbacks);

        // 初始化定时拉取并立即回调一次
        pullAndCallback();
        log.warn("SettingsAutoPullTask 初始化完成");
    }

    private void initAddListener(Class<?> realClazz, Object settingIns) {
        ReflectionUtils.doWithFields(realClazz, field -> {
            try {
                SettingValue annotation = field.getAnnotation(SettingValue.class);
                String name = StringUtils.isNotBlank(annotation.name()) ? annotation.name() : field.getName();
                String group = StringUtils.isNotBlank(annotation.group()) ? annotation.group() : field.getName();
                String description = StringUtils.trimToEmpty(annotation.description());
                log.info("initAddListener name={}, group={}", name, group);
                //初始化默认值
                try {
                    field.setAccessible(true);
                    Object defaultValue = field.get(settingIns);
                    if (defaultValue != null) {
                        SettingsLocalStorage.put(TenantEnum.ALL.getTenantId(), Constant.DEFAULT, group, name, defaultValue);
                    }
                } catch (Exception e) {
                    log.error("get setting default value exception", e);
                }

                // 注册监听回调
                Settings.registerRowListener(name, group,
                        settingsRow -> {
                            try {
                                String content = settingsRow.getContent();
                                String virtualRegion = settingsRow.getVirtualRegion();
                                Long tenantId = settingsRow.getTenantId();
//                                log.info("配置加载 virtualRegion={}, group={}, name={}, content={}", virtualRegion, settingsRow.getGroup(), settingsRow.getName(), settingsRow.getContent());

                                // 不支持 null 值，需要配置为明确的值
                                if (content == null || virtualRegion == null || group == null || name == null) {
                                    return 0;
                                }
                                // 如果配置的是未定义的值，也忽略，这个是初始化进去的（自己配置不能配这个值）
                                if (content.equals(Settings.UNDEFINED_CONTENT)) {
                                    return 0;
                                }
                                Object originValue = SettingsLocalStorage.get(tenantId, virtualRegion, group, name);
                                Object newValue = SettingsContentUtils.parseValue(content, field.getType(), field.getGenericType());
//                                log.info("配置比对 virtualRegion={}, group={}, name={}, originValue={} ,newValue:{}", virtualRegion, settingsRow.getGroup(), settingsRow.getName(), originValue,newValue);
                                if (newValue != null && !Objects.equals(originValue, newValue)) {
                                    log.info("配置更新 virtualRegion={}, group={}, name={}, content={}", virtualRegion, settingsRow.getGroup(), settingsRow.getName(), settingsRow.getContent());
                                    SettingsLocalStorage.put(tenantId, virtualRegion, group, name, newValue);
                                    return 1;
                                }
                                return 0;
                            } catch (Throwable t) {
                                log.error("回调异常 name={}, group={}, content={}", settingsRow.getName(), settingsRow.getGroup(), settingsRow.getContent(), t);
                                return 0;
                            }
                        });
                // 将默认值插入到数据库（仅插入，不更新），异步处理
                INIT_DEFAULT_VALUE_THREAD_POOL.submit(() -> {
                    try {
                        Long aLong = settingsManager.queryIdExists(name, group, Constant.DEFAULT);
                        if (aLong == null) {
                            Object originValue = null;
                            try {
                                field.setAccessible(true);
                                originValue = field.get(settingIns);
                            } catch (Exception e) {
                                log.error("set setting value exception", e);
                            }
                            Settings.SettingsRow settingsRow = new Settings.SettingsRow();
                            settingsRow.setName(name);
                            settingsRow.setGroup(group);
                            settingsRow.setContent(originValue == null ? Settings.UNDEFINED_CONTENT : SettingsContentUtils.stringifyValue(originValue, field.getType(), field.getGenericType()));
                            settingsRow.setDescription(description);
                            settingsRow.setVirtualRegion(Constant.DEFAULT);
                            settingsRow.setTenantId(TenantEnum.ALL.getTenantId());
                            settingsManager.create(settingsRow);
                        }
                    } catch (Exception ex) {
                        log.error("init setting error {} {} ", group, name, ex);
                    }

                });
            } catch (Exception e) {
                log.warn("register setting value consumer failed, fieldName={}", field.getName());
            }
        }, field -> {
            SettingValue annotation = field.getAnnotation(SettingValue.class);
            return annotation != null;
        });
    }

    private void pullAndCallback() {
        Runnable runnable = () -> {
            try {
                if (Boolean.FALSE.equals(Settings.getEnable())) {
                    log.warn("settings is disabled, consider config property 'vgk.settings.enable=true'.");
                    return;
                }
                Date startDate = new Date();
                long diff = System.currentTimeMillis() - preTimeMillis;
                //控制5分钟打印一次
                boolean needLog = diff > 5 * 60 * 1000;
                if (needLog) {
                    preTimeMillis = System.currentTimeMillis();
                    log.info("拉取配置开始，lastDate={} diff={}", lastDate, diff);
                }
                List<Settings.SettingsRow> deltaSettingsRows;
                if (lastDate == null) {
                    deltaSettingsRows = settingsManager.queryAll();
                } else {
                    deltaSettingsRows = settingsManager.queryDelta(lastDate);
                }
                int deltaTotal = deltaSettingsRows.size();
                // 合并增量的到全量的里面
                int effectTotal = Settings.mergeDeltaRowsAndCallback(deltaSettingsRows);
                if (deltaTotal > 0 || effectTotal > 0) {
                    log.info("拉取配置回调完成，deltaTotal={}, effectTotal={}", deltaTotal, effectTotal);
                }
                // 排序并记录最新的时间
                lastDate = sortAndReturnLastDate(startDate);
                if (needLog) {
                    log.info("拉取配置完成，lastDate={}", lastDate);
                }
                SettingsRepository settingsRepository = SpringUtils.getBean(SettingsRepository.class);
            } catch (Throwable t) {
                log.error("拉取配置失败", t);
            }
        };
        // 立即运行一次
        runnable.run();
        // 加入到周期调度中
        ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
        scheduledExecutorService.scheduleWithFixedDelay(runnable, 5, 5, TimeUnit.SECONDS);
    }

    private Date sortAndReturnLastDate(Date startDate) {
        // 排序并记录最新的时间
        AtomicReference<Date> lastDate = new AtomicReference<>();
        Settings.getSettingsRows().stream()
                .peek(item -> {
                    if (item.getGmtModified() == null) {
                        item.setGmtModified(new Date(0));
                    }
                })
                .max(Comparator.comparingLong(item -> item.getGmtModified().getTime()))
                .ifPresent(settingsRow -> {
                    Date gmtModified = settingsRow.getGmtModified();
                    // 修正，不能超过当前时间
                    if (gmtModified.after(startDate)) {
                        lastDate.set(startDate);
                    } else {
                        lastDate.set(gmtModified);
                    }
                });
        return lastDate.get();
    }
}
